#pragma once

class Queue
{
public:
	struct ELEMENT
	{
		int m_nThreadNum, m_nRequestNum;
	};
	typedef ELEMENT* PELEMENT;

private:
	PELEMENT m_pElements;
	int m_nMaxElements;
	HANDLE m_h[2];
	HANDLE &m_hmtxQ;
	HANDLE &m_hsemNumElements;
public:
	Queue(int nMaxElements);
	~Queue();
	BOOL Append(PELEMENT pElement, DWORD dwTimeout);
	BOOL Remove(PELEMENT pElement, DWORD dwTimeout);
};

/////////////////////////////////////////////////////////////////////////

Queue::Queue(int nMaxElements) : m_hmtxQ(m_h[0]), m_hsemNumElements(m_h[1])
{
	m_pElements = (PELEMENT)HeapAlloc(GetProcessHeap(), 0, sizeof(ELEMENT) * nMaxElements);
	m_nMaxElements = nMaxElements;
	m_hmtxQ = CreateMutex(NULL, FALSE, NULL);
	m_hsemNumElements = CreateSemaphore(NULL, 0, nMaxElements, NULL);
}

///////////////////////////////////////////////////////////////////////

Queue::~Queue()
{
	CloseHandle(m_hsemNumElements);
	CloseHandle(m_hmtxQ);
	HeapFree(GetProcessHeap(), 0, m_pElements);
}

///////////////////////////////////////////////////////////////////////

BOOL Queue::Append(PELEMENT pElement, DWORD dwTimeout)
{
	BOOL fOK = FALSE;
	DWORD dw = WaitForSingleObject(m_hmtxQ, dwTimeout);

	if(dw == WAIT_OBJECT_0)
	{
		LONG lPrevCount;
		fOK = ReleaseSemaphore(m_hsemNumElements, 1, &lPrevCount);
		if(fOK)
			m_pElements[lPrevCount] = *pElement;
		else
			SetLastError(ERROR_DATABASE_FULL);
		ReleaseMutex(m_hmtxQ);
	}
	else
	{
		SetLastError(ERROR_TIMEOUT);
	}

	return fOK;
}


///////////////////////////////////////////////////////////////////////


BOOL Queue::Remove(PELEMENT pElement, DWORD dwTimeout)
{
	BOOL fOK = (WaitForMultipleObjects(_countof(m_h), m_h, TRUE, dwTimeout) == WAIT_OBJECT_0);
	if(fOK)
	{
		*pElement = m_pElements[0];
		MoveMemory(&m_pElements[0], &m_pElements[1], sizeof(ELEMENT) * (m_nMaxElements - 1));
		ReleaseMutex(m_hmtxQ);
	}
	else
	{
		SetLastError(ERROR_TIMEOUT);
	}

	return fOK;
}